// 定义函数一共有四种方式

// 1：使用function来定义，在这里我们需要明确的指出函数参数的类型而函数的返回值呢可以通过TypeScript的类型推断省去
function add1(x: number, y: number) {
  return x + y;
}

// 2：第二种呢我们可以通过一个变量来定义一个函数类型
let add2: (x: number, y: number) => number;

// 3：此外还可以通过一个类型别名来定义一个函数类型
type add3 = (x: number, y: number) => number;

// 4：另外接口呢也可以定义一个函数类型
interface add4 {
  (x: number, y: number): number;
}

// 注意：
/**
 * 后面三种呢只是函数类型的定义并没有具体的实现，
 * 真正在调用的时候呢我们要书写这个函数体
 *  */

// TypeScript 对函数的参数有哪些要求
/**
 *
 * 在JavaScript中呢对函数参数的个数是没有限制的，而在TypeScript中呢形参和实参必须一一对应
 *
 * */

// 我们调用函数 add1，可以发现对于函数参数我们少一个不行多一个也不行
add1(1, 2);

// 那么有的时候我们需要参数是可选的，可以传也可以不传，这就用到了可选参数，在这里我们必须要注意一下可选参数必须位于必选参数之后
// ? 表示可选参数
function add5(x: number, y?: number) {
  return y ? x + y : x;
}
add5(1); // y的参数是可不传的

// 我们也可以像es6那样为参数提供一个默认值
// 这里我们需要注意下，在必传参数之后默认参数是不可以省略的，必须明确的传入undefined来获取它的默认值，在必选参数之后的默认值是可以不传的
function add6(x: number, y = 0, z: number, q = 1) {
  return x + y + z + q;
}
const value6 = add6(1, undefined, 3);
console.log("value6：", value6); // 输出：5

// 那么以上函数参数的个数呢都是固定的，当参数不确定的时候我们就可以使用剩余参数
// 我们把后面的参数定义为剩余参数和es6里面是一样的使用...，剩余参数的类型呢是以数组类型存在的
function add7(x: number, ...rest: number[]) {
  return x + rest.reduce((pre, cur) => pre + cur);
}
const value7 = add7(1, 2, 3, 4, 5);
console.log("value7：", value7); // 输出：15


// 接下来呢我们介绍下函数重载
/**
 * 比如在C++或者Java都有函数重载的概念，它们的含义呢如果两个函数名称相同，但是函数参数个数或者参数类型不同那么就是那么就实现了一个函数重载。
 * 函数重载的好处呢是不需要为了相似功能的函数选用不同的函数名称这样增强了函数的可读性。
 *
 * 在TypeScript中函数重载的定义有一些不同，比如我们要实现这样一个重载，函数呢支持多个参数如果参数都是数字那么就返回所有参数的和如果都是字符串那么就返回所有字符串的拼接。
 * TypeScript的函数重载要求我们先定义一系列名称相同的函数申明。
 * */

// 申明函数重载
function add8(...rest: number[]): number;
function add8(...rest: string[]): string;

// 实现上面的重载，TypeScript要求我们在一个类型最宽泛的版本中实现这个重载
function add8(...rest: any[]): any{
  // 它的类型是最宽泛的是any，返回值也是 any
  // 在这里我们就来实现这个函数
  let first = rest[0];
  if(typeof first === 'string'){
    return rest.join('');
  }
  if(typeof first === 'number'){
    return rest.reduce((pre, cur) => pre + cur); // 返回各个元素的和
  }
}

console.log(add8(1, 2, 3));
console.log(add8('a', 'b', 'c'));

// TypeScript 编译器在处理重载的时候会去查询一个重载的列表也就我们上面定义的两个 add8 重载函数定义，
// 并且会尝试第一个定义如果匹配的话那么就使用第一个重载函数定义如果不匹配的话那么就接着往下查找，
// 所以呢我们要把最容易匹配的函数定义写在最前面。

export {};
